var $ = window.$;
var event = require('../../core/event');
var timer = require('../../core/timer');
var sample = require('../sample/manager');
var notify = require('../notify/ui');
var frac = require('../../util/frac');
var Log = require('../../util/logger');
var util = require('../../util/index');
var input = require('../../core/input');
var jade = require('jade');
var sound = require('../audio/sound');
var timing = require('../timing/manager');
var base = require('./ui.js');
var config = require('../../core/config');
var inherits = require('util').inherits;
var enums = require('../../core/enum');
var document = window.document;

var GridUITaiko = function (){};
inherits(GridUITaiko, base);
/**
 * 轨道定义：
 * 0 1 2 1p(普通 达人 玄人)
 * 3 4 5 2p(普通 达人 玄人)
 * 6 音乐/音效 同时满足note.type = 1，非音效无type值
 *
 * timing定义
 * multiplier 绿线，取值为基础卷动速度的多少倍，默认为1，变速后需要插入一条1的绿线还原
 *
 * control（并列与timing的数组）
 * type = 0 谱面分歧
 *
 * note.style定义
 * 0小红
 * 1小蓝
 * 2大红
 * 3大蓝
 * 4小黄连打
 * 5大黄连打
 * 6气球连打
 * 7地瓜连打
 *
 * note.groupstart 此note击打后开启额外note
 * note.group = 1-n 分组，开启额外note那个最先发光的也要属于对应分组内，敲击后整组都出现
 *
 * 连打有endbeat表示连打持续的时间
 * 气球和地瓜额外属性hits表示提前移除所需要的敲击数
 */
GridUITaiko.prototype.init = function(){
    if (this._inited) {
        return;
    }
    this.noteInfo = util.loadJsonSync("./data/taiko_tools.json");
    this.parent = $('#edit_up');
    var tmpl = jade.compileFile('./tmpl/grid/taiko.jade');
    this.parent.html(tmpl());
    this.editArea = $("#taiko_edit_area");
    this.toolBar = $("#edit_tool");
    this.pluginTaikoEdit = require('./plugin/edit-taiko');
    this.pluginTaikoEdit.setUI(this);
    this.pluginTaikoCopy = require('./plugin/copy-taiko');
    this.pluginTaikoCopy.setUI(this);
    var meta = require("./plugin/meta");
    this.pluginMeta = new meta();
    this.pluginMeta.init();
    this.pluginMeta.setUI(this);
    this.resource = document.getElementById("taiko_resource");
    this.gridScale = 1.0;  //1 beat的缩放值
    this.divide = 4;
    this._beatW = 160; //格子的基础宽度
    this._gridLeftX = 50; //引导线距离左侧的距离
    this.gridScaleW = this._beatW * this.gridScale; //缩放后一个格子的宽度
    this.gridLineHeight = 321; //主刻度的高度
    this.difficuty = 0; //谱面难度 用于谱面分歧用
    this.trackWidth = $("#edit_up").width();
    this.cavOffTop = $("#edit_up").offset().top;
    this.select = $("#select_area");
    this.cavMain = document.getElementById('cav_main').getContext('2d');
    this.cavMask = document.getElementById('cav_mask').getContext('2d');
    this.playedNote = [];
    this.playingNote = [];
    this.unplayedNote = [];
    this.noteMaskMap = {};
    this.selectedNotes = {};
    this.colorDelta = 0; //掩膜层的note颜色计数器
    this.firstInit = true;//第一次绘制要延迟 因为素材还没加载进来
    this.bindEvents();
    this.initi18n();
    $("body").addClass("taiko_mode");
};

GridUITaiko.prototype.clear = function(){

};

GridUITaiko.prototype.bindEvents = function(){
    var cav = $("#cav_main");
    cav.on('mousedown', this.onTaikoMouseEvent.bind(this));
    cav.on('mousemove', this.onTaikoMouseEvent.bind(this));
    cav.on('mouseup', this.onTaikoMouseEvent.bind(this));
    this.parent.on('mousewheel', this.onParentEvent.bind(this));
    event.bind(event.events.key.up, 'taiko_ui', this);
    event.bindOwner('global', 'taiko_ui', this);
    event.bindOwner('grid', 'taiko_ui', this);
    var self = this;
    $("#select_area").on('mouseup',function(e){
        //解决从右下到左上划框触发div而不是canvas事件的问题
        self.onTaikoMouseEvent(e);
    });
    //分歧难度选择
    $("#toggle_divergence").on("click", "span", function(e){
        $(this).parent().children(".curr").removeClass("curr");
        $(this).addClass("curr");
        self.difficuty = $(this).index() - 1;
        self.fullUpdateNotes();
    });
    this.timerId = timer.registUpdate(this.onUpdate.bind(this), 60);
};

GridUITaiko.prototype.initi18n = function(){
    $("#edit_tool").children(".g_button").each(function(idx, obj){
        obj = $(obj);
        obj.attr("title", config.i18n("taiko" + obj.attr("data-index")));
    });
    $("#track_t1").html(config.i18n("taiko10"));
    $("#track_t2").html(config.i18n("taiko11"));
    $("#track_t3").html(config.i18n("taiko12"));
    $("#title_divergence").html(config.i18n("taiko16"));
    $("#mode_normal").html(config.i18n("taiko17"));
    $("#mode_advance").html(config.i18n("taiko18"));
    $("#mode_expert").html(config.i18n("taiko19"));
};

GridUITaiko.prototype.startMaskTimer = function(){
    if(this.maskTimer){
        return;
    }
    this.lastMaskTime = -1;
    this.maskDrawn = false;
    var self = this;
    this.maskTimer = setInterval(function(){
        var prev = self.lastUpdate;
        if(prev != self.lastMaskTime){
            self.maskDrawn = false;
            self.lastMaskTime = prev;
        }else{
            if(!self.maskDrawn){
                self.drawNoteMasks();
                self.maskDrawn = true;
            }
        }
    },50);
};

GridUITaiko.prototype.onEvent = function(e, param){
    if(e == event.events.key.up) {
        if (!this.focus) {
            return;
        }
        switch (param.key) {
            case 27: //esc
                this.cancelSelect();
                this.stopPlay();
                return event.result.stopAll;
            case 37: //<--
                break;
            case 38: // 上
                this.onScroll(e, 1, true);
                break;
            case 39:// -->
                break;
            case 40://下
                this.onScroll(e, -1, true);
                break;
            case 8: //backspace
            case 46://del
                this.deleteSelect();
                return event.result.stopAll;
            case enums.KEY.A:
                this.selectAll();
                return event.result.stopAll;
        }
    }else if(e == event.events.global.copy) {
        if (!this.focus) {
            return;
        }
        this.pluginTaikoCopy.onCopy();
    }else if(e == event.events.global.paste){
        if (!this.focus) {
            return;
        }
        this.pluginTaikoCopy.onPaste();
    }
};

GridUITaiko.prototype.onTaikoMouseEvent = function(e){
    e.stopPropagation();
    //因为可能性能问题 播放的时候不刷新掩膜层 因此无法进行编辑操作
    if(this.isPlaying()){
        return;
    }
    switch(e.type){
        case 'mousedown':
            if(!this.focus) {
                this.focus = true;
            }
            this.pluginTaikoEdit.onTaikoMouseDown(e);
            break;
        case 'mousemove':
            this.pluginTaikoEdit.onTaikoMouseMove(e);
            break;
        case 'mouseup':
            this.pluginTaikoEdit.onTaikoMouseUp(e);
            break;
    }
};

GridUITaiko.prototype.onUpdate = function(){
    var time = sound.stableTime();
    if(!this.needUpdate && this.lastUpdate == time){
        return true;
    }
    var self = this;
    if(this.firstInit){
        setTimeout(function(){
            self.drawNotes();
            self.startMaskTimer();
        },200);
        self.firstInit = false;
    }else{
        this.drawNotes();
    }
    this.needUpdate = false;
    this.lastUpdate = time;
};
/*播放控制相关函数*/

GridUITaiko.prototype.sync = function(isMce){
    this.initSeekQueue();
    var self = this;
    if(isMce){
        this.needUpdate = true;
    }
    self.onUpdate();
};

GridUITaiko.prototype.isPlaying = function(){
    return $("#control").children('[data-action="play"]').hasClass("press");
};

GridUITaiko.prototype.initSeekQueue = function(){
    var notes = this.manager.getNotes();
    var note;
    this.unplayedNote = [];
    this.playedNote = [];
    this.playingNote = [];
    this.lastSeekTime = -1;
    for(var i =0;i<notes.length;i++){
        note = notes[i];
        //把一个note拆成两份头和尾
        if(!note.endbeat){
            this.unplayedNote.push({
                realNote : note,
                _beat : timing.beatToFloat(note.beat)
            });
        }else{
            this.unplayedNote.push({
                realNote : note,
                _beat : timing.beatToFloat(note.beat)
            });
            this.unplayedNote.push({
                realNote : note,
                _beat : timing.beatToFloat(note.endbeat)
            });
        }
    }
    console.log("init seek queue with obj length = " + (this.unplayedNote.length + this.playedNote.length));
    this.unplayedNote.sort(function(a, b){
        return a._beat - b._beat > 0 ? 1 : -1
    });
};

GridUITaiko.prototype.updateGrid = function(){
    this.gridScaleW = this._beatW * this.gridScale;
    this.fullUpdateNotes();
};

GridUITaiko.prototype.updateNotes = function(){

};

GridUITaiko.prototype.updateSeekQueue = function(floatBeat){
    var note;
    var currTime = sound.stableTime();
    var startBeat = floatBeat - this._gridLeftX / this.gridScaleW;
    var endBeat = startBeat + this.trackWidth / this.gridScaleW;
    if(currTime > this.lastSeekTime){
        //播放或者前跳
        while(this.playingNote.length > 0){
            note = this.playingNote[0];
            if(note._beat < startBeat){
                this.playedNote.push(this.playingNote.shift());
            }else{
                break;
            }
        }
        while(this.unplayedNote.length > 0){
            note = this.unplayedNote[0];
            if(note._beat < endBeat){
                this.playingNote.push(this.unplayedNote.shift());
            }else{
                break;
            }
        }
    }else{
        //后跳
        while(this.playingNote.length > 0){
            note = this.playingNote[this.playingNote.length - 1];
            if(note._beat > endBeat){
                this.unplayedNote.unshift(this.playingNote.pop());
            }else{
                break;
            }
        }
        while(this.playedNote.length > 0){
            note = this.playedNote[this.playedNote.length - 1];
            if(note._beat > startBeat){
                this.playingNote.unshift(this.playedNote.pop());
            }else{
                break;
            }
        }
    }
    this.lastSeekTime = currTime;
};

GridUITaiko.prototype.startEdgeSeek = function(step){
    this.seekStep = step;
    var self = this;
    if(!this.seekTimer){
        this.seekTimer = setInterval(function(){
            var stableTime = sound.stableTime();
            if((self.seekStep < 0 && stableTime > 0) || (self.seekStep > 0 && stableTime < sound.getMaxLength())){
                var beat = timing.timeToBeat(stableTime, self.divide);
                beat = frac.add(beat, [0, self.seekStep, 4]);//最小移动1/4拍
                sound.seekTo(timing.beatToTime(beat));
            }
        },250);
    }
};

GridUITaiko.prototype.stopEdgeSeek = function(){
    if(this.seekTimer){
        clearInterval(this.seekTimer);
        this.seekTimer = null;
    }
};

/*坐标、轨道换算相关函数*/

GridUITaiko.prototype.mousePosToBeat = function(e){
    var x = e.pageX - this._gridLeftX;
    var now = sound.stableTime()
    var floatBeat = timing.timeToFloatBeat(now);
    var beat = frac.fromFloat(floatBeat + x / this.gridScaleW, this.divide);
    return beat;
};

GridUITaiko.prototype.absPosToCanvas = function(pos){
    return [pos[0], pos[1] - this.cavOffTop];
};

GridUITaiko.prototype.getColumn = function(e){
    var y = e.pageY - this.cavOffTop;
    if(y > 80 && y < 144){
        return this.difficuty;
    }else if(y > 170 && y<234){
        return 3 + this.difficuty;
    }else if(y> 260 && y < 324){
        return 6;
    }
    else{
        return -1;
    }
};

GridUITaiko.prototype.getNoteX = function(note){
    var floatNow = timing.timeToFloatBeat(sound.stableTime());
    var floatOffset = frac.toFloat(note.beat) - floatNow;
    return this._gridLeftX + floatOffset * this.gridScaleW;
};

/**
 * 将y区间换算为column的区间(判断轨道中线)
 * @param yStart
 * @param yEnd
 */
GridUITaiko.prototype.yRangeToColumn = function(yStart, yEnd){
    var tracks = this.editArea.children(".track");
    var colStart = -1;
    var colEnd = -1;
    for(var i=0;i<tracks.length;i++){
        var track = $(tracks[i]);
        var bTop = track.position().top + track.height() / 2;
        if(yStart < bTop && colStart == -1){
            colStart = i;
        }
        if(yEnd > bTop){
            colEnd = i;
        }
    }
    var tmp = {};
    for(var i=colStart;i<=colEnd;i++){
        if(i == 0){
            tmp[this.difficuty] = true;
        }else if(i == 1){
            tmp[3 + this.difficuty] = true;;
        }else if(i == 2){
            tmp[6] = true;
        }
    }
    return tmp;
};


/*--------绘图相关----------*/

/**
 * 绘制一个note
 * column = 0 主轨道
 * column = 1 2p
 * column >= 2 音轨
 * @param note
 * @param isMask 是否是画掩膜层
 */
GridUITaiko.prototype.drawNote = function(note, isMask){
    if(note.column != 6 && note.column != this.difficuty  && note.column != this.difficuty  + 3){
        //谱面分歧下只画n 3+n 6这几个轨道
        return;
    }
    //音乐轨固定画小蓝脸
    var nStyle = note.column == 6 ? 1 : note.style;
    var info = this.noteInfo[nStyle];
    var pw,ph,px,py;//掩膜层的x y w h
    var dyArr = [0, 0, 0, 90, 90, 90, 180];
    if(!info){
        return false;
    }
    var topDelta = this.selectedNotes[note.id] ? 64 : 0;
    if(isMask){
        topDelta = 128;
    }
    var dy = dyArr[note.column];
    var cav = isMask ? this.cavMask : this.cavMain;

    if(nStyle == 4 || nStyle ==5) {
        //计算note的长度
        var db = frac.subtract(note.endbeat, note.beat);
        //这里的hw实际上是延长段的距离 因为头尾是半个伸出来不算在beat长度内
        var hw = timing.beatToFloat(db) * this.gridScaleW;
        //三段拼起来的连打
        var w1 = info.p1width / 2; //头
        var w3 = info.p3width; //尾
        var nx = this.getNoteX(note);
        py = 110 - info.height/2 + dy;
        pw = hw + w1 + w3;
        px = nx;
        ph = info.height;
        cav.drawImage(this.resource, info.p1left, info.p1top + topDelta, info.p1width / 2, info.height, px - w1, py, w1, ph);
        cav.drawImage(this.resource, info.p2left, info.p2top + topDelta, info.p2width, info.height, px, py, hw, ph);
        cav.drawImage(this.resource, info.p3left, info.p3top + topDelta, w3, info.height, px + hw, py, w3, ph);
    }else{
        px = this.getNoteX(note) - info.centerX + 2;
        py = 110 - info.centerX + dy;
        pw = info.width;
        ph = info.height;
        if(nStyle == 6){
            //气球的掩膜层

        }
        cav.drawImage(this.resource, info.left, info.top + topDelta, info.width, info.height, px, py, pw, ph);
        if(nStyle == 6){
            //画气球上的击打数
            this.drawText(note.hits, "12px", "#fff", px + 64, py + 20);

        }
    }
    if(!isMask){
        return;
    }
    var imageData = this.cavMask.getImageData(px, py, pw, ph);
    var data = imageData.data;
    //改变掩膜层的颜色 用颜色区分点到的是什么物件
    var deltaArr = this.numToRGBArray(this.colorDelta);
    var i, j, t;
    for(i=0;i<pw;i++){
        for(var j=0;j<ph;j++){
            t = (i * ph + j) * 4;
            if(data[t] == 255){
                data[t] -= deltaArr[0];
                data[t+1] -= deltaArr[1];
                data[t+2] -= deltaArr[2];
            }
        }
    }
    var key = note.column + "_" + (255 - deltaArr[1]) + "_" + (255-deltaArr[2]);
    this.noteMaskMap[key] = note;
    this.cavMask.putImageData(imageData,px, py);
    this.colorDelta += 5;
};

GridUITaiko.prototype.drawNoteMasks = function(){
    this.noteMaskMap = {};
    this.colorDelta = 100;
    this.cavMask.clearRect(0, 0, this.trackWidth, 400);
    for(var i=0;i<this.playingNote.length;i++){
        this.drawNote(this.playingNote[i].realNote, true);
    }
};

GridUITaiko.prototype.drawText = function(text, size, color, x, y){
    var ctx = this.cavMain;
    ctx.font = size + " Arial normal";
    ctx.fillStyle = color;
    ctx.fillText(text, x, y);
}

/**
 * 把一个数转成rgb上的三个分量
 */
GridUITaiko.prototype.numToRGBArray = function(num){
    var r = Math.floor(num >> 16);
    var g = Math.floor((num - r * 65536) / 256);
    var b = num % 65536;
    return [r, g, b];
};

GridUITaiko.prototype.drawNotes = function(){
    var time = sound.stableTime();
    var floatBeat = timing.timeToFloatBeat(time);
    this.updateSeekQueue(floatBeat);
    this.cavMain.clearRect(0, 0, this.trackWidth, 400);
    this.drawGridLine(floatBeat);
    this.drawBpm(floatBeat);
    for(var i=0;i<this.playingNote.length;i++){
        var realNote = this.playingNote[i].realNote;
        this.drawNote(realNote);
    }
};

GridUITaiko.prototype.drawBpm = function(floatBeat){
    var timeTable = timing.getAll();
    var startBeat = Math.floor(floatBeat) - Math.ceil(this._gridLeftX / this.gridScaleW) - 1;
    var currX = this._gridLeftX;
    var lines = [];
    for(var i=0;i<timeTable.length;i++){
        var item = timeTable[i];
        var beat = timing.beatToFloat(item.beat);
        if(beat < startBeat){
            continue;
        }
        var x = parseInt(currX + (beat - floatBeat) * this.gridScaleW) + 0.5;
        if(x > this.trackWidth){
            break;
        }
        if(item.bpm){
            this.drawText(parseInt(item.bpm), "14px", "#ef6666", x+4, 60);
        }
        if(item.multiplier){
            this.drawText(item.multiplier + "x", "14px", "#00CC66", x+(item.bpm ? 28 : 4), 60);
        }
        lines.push([x, 50, x, this.gridLineHeight]);
    }
    this.drawLineGroup(lines, "#ef6666");
};

GridUITaiko.prototype.drawGridLine = function(floatBeat){
    var startBeat = Math.floor(floatBeat) - Math.ceil(this._gridLeftX / this.gridScaleW) - 1;
    var currX = this._gridLeftX;
    var l1 = [];  //四组颜色的线
    var l2 = [];
    var l3 = [];
    var l4 = [];
    var loopStop = false;
    var beatNumbers = [];//写在线上的节拍计数
    var beat = startBeat;

    while(!loopStop){
        beat ++;
        if(beat<0){
            continue;
        }
        for(var i=0;i<this.divide;i++){
            var x = parseInt(currX + (beat - floatBeat + i / this.divide) * this.gridScaleW) + 0.5;
            if(x > this.trackWidth){
                loopStop = true;
                break;
            }
            if(i == 0){
                beatNumbers.push([beat+1, x]);
                l1.push([x, 60, x, this.gridLineHeight + 0.5]);
                this.drawText(beat,"14px","#fff",x + 4,76);
            }else{
                var c = frac.gcd(i, this.divide);
                var _c = this.divide / c;
                var pos = [x, 82, x, this.gridLineHeight + 0.5];
                if(_c == 2){
                    l2.push(pos);
                }else if(_c == 3){
                    l3.push(pos);
                }else if(_c == 4){
                    l4.push(pos);
                }else{
                    l1.push(pos);
                }
            }
        }
    }

    this.drawLineGroup(l1, "#c4c4c4");
    this.drawLineGroup(l2, "#66cdef");
    this.drawLineGroup(l3, "#66efaa");
    this.drawLineGroup(l4, "#ef66ee");
    //清理掉两条轨道之间多余的线
    this.cavMain.clearRect(0,142,this.trackWidth,30);
    this.cavMain.clearRect(0,232,this.trackWidth,30);
};

GridUITaiko.prototype.drawLineGroup = function(group, color){
    var cav = this.cavMain;
    cav.lineWidth = 1;
    cav.strokeStyle = color;
    cav.beginPath();
    for(var i=0;i<group.length;i++){
        var pos = group[i];
        cav.moveTo(pos[0], pos[1]);
        cav.lineTo(pos[2], pos[3]);
    }
    cav.stroke();
};

/*Note操作函数*/
GridUITaiko.prototype.cancelSelect = function(){
    this.selectedNotes = {};
    this.drawNotes();
    this.pluginMeta.update();
};

GridUITaiko.prototype.markNoteSelected = function(note){
    var id = note.id;
    this.selectedNotes[id] = !this.selectedNotes[id];
};

GridUITaiko.prototype.getNoteAtMousePos = function(e){
    var x = e.pageX;
    var y = e.pageY - this.cavOffTop;
    var data = this.cavMask.getImageData(x, y, 1, 1).data;
    var key = this.getColumn(e) + "_" + data[1] + "_" + data[2];
    return this.noteMaskMap[key];
};

GridUITaiko.prototype.createNoteAtMousePos = function(e){
    var beat = this.mousePosToBeat(e);
    var column = this.getColumn(e);
    var style = +$("#edit_tool").children(".press").attr("data-index");
    return {
        beat : beat,
        column : column,
        style : style
    };
};

GridUITaiko.prototype.deleteNote = function(note){
    if(note.sound){
        sound.removeCell(note.tid);
    }
    this.manager.removeNote(note.id);
    this.fullUpdateNotes();
};

GridUITaiko.prototype.deleteSelect = function(){
    for(var k in this.selectedNotes){
        var note = this.manager.getNote(k);
        if(note.sound){
            sound.removeCell(note.tid);
        }
        this.manager.removeNote(k);
    }
    this.fullUpdateNotes();
};

GridUITaiko.prototype.fullUpdateNotes = function(){
    this.initSeekQueue();
    this.drawNotes();
    this.drawNoteMasks()
};

GridUITaiko.prototype.getSelectCount = function(){
    var i = 0;
    for(var k in this.selectedNotes){
        i++;
    }
    return i;
};

GridUITaiko.prototype.selectRectNotes = function(startBeat, endBeat, yStart, yEnd){
    var notes = this.manager.getNotes();
    var colRange = this.yRangeToColumn(yStart, yEnd);
    for(var i=0;i<notes.length;i++){
        var note = notes[i];
        if(frac.compare(startBeat, note.beat) < 0 && frac.compare(endBeat, note.beat) > 0) {
            if(colRange[note.column]){
                this.selectedNotes[note.id] = true;
            }
        }else if(frac.compare(note.beat, endBeat) > 0){
            break;
        }
    }
};

GridUITaiko.prototype.adjustNoteParams = function(note){
    if(note.style == 6){
        //气球消失所需要的击打数
        var hitsVal = note.hits || 10;
        var hits = window.prompt(config.i18n("taiko13"), hitsVal);
        hitsVal = parseInt(hits);
        note.hits = hitsVal != NaN ? hitsVal : 10;
        //气球持续的时间
        var endbeat = note.endbeat || frac.add(note.beat, [2,0,1]);
        var dbeat = frac.subtract(endbeat, note.beat);
        var lastBeatsVal = dbeat[1] == 0 ? dbeat[0] : (dbeat[0] + ":" + dbeat[1] + "/" + dbeat[2]);
        var lastBeats = window.prompt(config.i18n("taiko14"), lastBeatsVal);
        if(lastBeats.indexOf(":") > 0){
            var tmp = lastBeats.match(/(\d+):(\d+)\/(\d+)/);
            lastBeatsVal = [tmp[1], tmp[2], tmp[3]];
        }else{
            lastBeatsVal = [+lastBeats,0,1];
        }
        note.endbeat = frac.add(note.beat, lastBeatsVal);
    }
};

GridUITaiko.prototype.getSelectIds = function(){
    var ret = [];
    for(var k in this.selectedNotes){
        ret.push(k);
    }
    return ret;
};

GridUITaiko.prototype.selectAll = function(){
    var notes = this.manager.getNotes();
    for(var i=0;i<notes.length;i++){
        this.selectedNotes[notes[i].id] = true;
    }
    this.drawNotes();
};

/**
 * 根据轨道和beat查找note
 * @param note
 */
module.exports = GridUITaiko;